#include "microtodo.h"
#include "ui_microtodo.h"
#include "ClickableLabel.h"
#include "server.h"
#include <QPainter>
#include <QDesktopServices>
#include <QFontDatabase>
#include <QMouseEvent>
#include <QCloseEvent>
#define CLIENT_ID "0fdf9736-7958-4d84-8f43-f8c116559686"
#define SCOPE "Tasks.ReadWrite offline_access"
#define GRANT_TYPE "authorization_code"
#define CLIENT_SECRET "oEI7Q~vKDTRDBw19q~kftw59~qJtJF1OpE8Rb"

QString ClickableLabel::listID;

MicroToDo::MicroToDo(QApplication* mainApp,QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MicroToDo)
{
    this->mainApp = mainApp;
    ui->setupUi(this);
    this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground, true);
    this->setAttribute(Qt::WA_DeleteOnClose, true);
    connect(ui->ExitLabel, &ClickableLabel::clicked, this, &MicroToDo::exit);
    //ui->ExitLabel->hide();
    connect(ui->SettingLabel, &ClickableLabel::clicked, this, &MicroToDo::showSettings);

    req = new HttpRequest();

    QString fontName = "微软雅黑";
    unsigned char fr = 255, fg = 255, fb = 255;
    bool bold = true, italic = false;
    int size_ = 0;
    FILE* dbfile = fopen("MicroToDo.db", "r");
    db = QSqlDatabase::addDatabase("QSQLITE", "MicroToDo_CONNECTION");
    db.setDatabaseName("MicroToDo.db");
    db.open();
    query = new QSqlQuery(db);
    if(!dbfile){
        s = new Server();
        s->start(65412);
        QDesktopServices::openUrl(QUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=0fdf9736-7958-4d84-8f43-f8c116559686&response_type=code&redirect_url=http://localhost:65412/auth&response_mode=query&scope=Tasks.ReadWrite offline_access"));

        QEventLoop loop;
        connect(s, &Server::readDone, &loop, &QEventLoop::quit);
        loop.exec();

        QString code = s->getCode();

        req->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
        QString res = req->Post("https://login.microsoftonline.com/common/oauth2/v2.0/token", QString("client_id=") + CLIENT_ID + "&scope=" + SCOPE + "&code=" + code + "&grant_type=" + GRANT_TYPE + "&client_secret=" + CLIENT_SECRET);
        QJsonDocument json = QJsonDocument::fromJson(res.toLocal8Bit().data());
        token = json.object().find("access_token")->toString();
        refresh_token = json.object().find("refresh_token")->toString();
        //qDebug() << json;

        query->exec("CREATE TABLE token(id INTEGER PRIMARY KEY, token VARCHAR(2000) NOT NULL, refreshToken VARCHAR(2000) NOT NULL)");
        query->exec(QString("INSERT INTO token(id, token, refreshToken) VALUES (1, '%1', '%2')").arg(token).arg(refresh_token));
        ClickableLabel::token = token;
        query->exec("CREATE TABLE form(r INTEGER PRIMARY KEY, g INTEGER, b INTEGER, alpha INTEGER, width INTEGER, height INTEGER)");
        query->exec("INSERT INTO form(r, g, b, alpha, width, height) VALUES(50, 50, 50, 128, 700, 500)");
        this->changeColor(50, 50, 50, 128, true);

        query->exec("CREATE TABLE fonts(name VARCHAR(50) PRIMARY KEY, use INTEGER, size INTEGER, r INTEGER, g INTEGER, b INTEGER, bold INTEGER, italic INTEGER)");
        QStringList name;
        QFontDatabase database;
        foreach (const QString &family, database.families(QFontDatabase::SimplifiedChinese)){
            name << family;
        }
        query->prepare("INSERT INTO fonts(name, use,size,r, g, b, bold, italic) values(:name, :use,-1, 255, 255, 255, 1, 0)");
        foreach(QString i, name){
            query->bindValue(":name", i);
            if(i == "微软雅黑")
                query->bindValue(":use", 1);
            else
                query->bindValue(":use", 0);
            query->exec();
        }
        query->exec("CREATE TABLE place(x INTEGER PRIMARY KEY, y INTEGER)");
        query->exec(QString("Insert into place(x, y) values (0, 0)"));

        req->setHeader("Authorization", token);
        req->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        std::string str = "{'displayName':'MicroToDo'}";
        QJsonObject js{{"displayName","MicroToDo"}};
        res = req->PostJson("https://graph.microsoft.com/v1.0/me/todo/lists", js);
        json = QJsonDocument::fromJson(res.toLocal8Bit().data());
        listId = json.object().find("id")->toString();
        ClickableLabel::listID = listId;
        query->exec("CREATE TABLE listId(id INTEGER PRIMARY KEY, listId VARCHAR(2000) NOT NULL)");
        query->exec(QString("INSERT INTO listId(id, listId) VALUES (1, '%1')").arg(listId));
        query->exec("CREATE TABLE tasks(id VARCHAR(2000), title VARCHAR(2000) NOT NULL, importance VARCHAR(20), isReminderOn INTEGER, status VARCHAR(20), Query INTEGER PRIMARY KEY)");
        query->exec("CREATE TABLE nextLink(deltaLink VARCHAR(2000) PRIMARY KEY)");
        query->exec(QString("Insert into nextLink(deltaLink) values('https://graph.microsoft.com/v1.0/me/todo/lists/%1/tasks/delta')").arg(listId));
    }else{
        fclose(dbfile);
        query->exec("Select listId from listId");
        query->next();
        listId = query->value(0).toString();
        ClickableLabel::listID = listId;
        query->exec("Select token,refreshToken from token where id = 1");
        query->next();
        token = query->value(0).toString();
        ClickableLabel::token = token;
        refresh_token = query->value(1).toString();
        refreshToken();
        query->exec("Select r,g,b,alpha,width,height from form");
        query->next();
        this->changeColor(query->value(0).toInt(), query->value(1).toInt(), query->value(2).toInt(), query->value(3).toInt(), false);
        this->resize(query->value(4).toInt(), query->value(5).toInt());
        query->exec("Select name,size,r,g,b,bold,italic from fonts where use=1");
        query->next();
        fontName = query->value(0).toString();
        size_ = query->value(1).toInt();
        fr = query->value(2).toInt();
        fg = query->value(3).toInt();
        fb = query->value(4).toInt();
        bold = query->value(5).toBool();
        italic = query->value(6).toBool();
        query->exec("Select x,y from place");
        query->next();
        move(query->value(0).toInt(), query->value(1).toInt());
        refreshData();
    }
    setFont(fontName, fr, fg, fb, size_, bold, italic, false);
    ui->ExitLabel->adjustSize();
    ui->SettingLabel->adjustSize();
    ui->flagArea->setAttribute(Qt::WA_TranslucentBackground, true);
    //ui->flagArea->setStyleSheet("background-color: rgba(0, 0, 0, 0)");
    connect(&timer, &QTimer::timeout, this, &MicroToDo::getData);
    connect(&refreshTimer, &QTimer::timeout, this, &MicroToDo::refreshToken);
    timer.start(500);
    refreshTimer.start(1800000);
}

void MicroToDo::refreshToken(){
    req->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    QString res = req->Post("https://login.microsoftonline.com/common/oauth2/v2.0/token", QString("client_id=") + CLIENT_ID + "&scope=" + SCOPE + "&refresh_token=" + refresh_token + "&redirect_url=http://localhost:65412/auth&grant_type=refresh_token&client_secret=" + CLIENT_SECRET);
    QJsonDocument json = QJsonDocument::fromJson(res.toLocal8Bit().data());
    token = json.object().find("access_token")->toString();
    ClickableLabel::token = token;
    refresh_token = json.object().find("refresh_token")->toString();
    if(!token.isEmpty() && !refresh_token.isEmpty())
        query->exec(QString("Update token SET token='%1',refreshToken='%2'").arg(token).arg(refresh_token));
}

MicroToDo::~MicroToDo()
{
    query->exec(QString("UPDATE place SET x=%1,y=%2").arg(this->x()).arg(this->y()));
    query->exec("update tasks set query=query-(select min(query) from tasks)");
    db.close();
    if(s)
        delete s;
    delete query;
    delete req;
    delete ui;
}

void MicroToDo::changeColor(unsigned char r, unsigned char g, unsigned char b, unsigned char alpha, bool changeDb){
    if(changeDb){
        query->exec(QString("UPDATE form SET r=%1,g=%2,b=%3,alpha=%4,width=%5,height=%6").arg(r).arg(g).arg(b).arg(alpha).arg(this->size().width()).arg(this->size().height()));
    }
    QPalette pal = palette();
    pal.setBrush(QPalette::Background, QColor(r, g, b, alpha));
    ui->widget->setPalette(pal);
    ui->widget->setAutoFillBackground(true);
}

void MicroToDo::setFont(QString fontName, unsigned char r, unsigned char g, unsigned char b, int size_, bool bold, bool italic, bool changeDb){
    if(changeDb){
        query->exec("UPDATE fonts SET use = 0 where use = 1");
        query->exec(QString("UPDATE fonts SET use = 1 where name='%1'").arg(fontName));
        query->prepare("UPDATE fonts SET r=:r,g=:g,b=:b,size=:size,bold=:bold,italic=:italic where use=1");
        query->bindValue(":r", r);
        query->bindValue(":g", g);
        query->bindValue(":b", b);
        query->bindValue(":size", size_);
        query->bindValue(":bold", bold);
        query->bindValue(":italic", italic);
        query->exec();
    }
    mainApp->setFont(QFont(fontName, size_, bold ? QFont::Bold : -1, italic));

    QPalette pal = ui->widget->palette();
    pal.setColor(QPalette::WindowText, QColor(r, g, b));
    ui->widget->setPalette(pal);
    QString style = QString("color: rgb(%1, %2, %3);background-color: rgba(255, 255, 255, 0);").arg(r).arg(g).arg(b);
    ui->scrollArea->setStyleSheet(style);
    ui->addLine->setStyleSheet(style);
    ui->addLabel->setStyleSheet(style);
}

void MicroToDo::mousePressEvent(QMouseEvent *event){
    if(event->button() == Qt::LeftButton){
        moving = true;
        reltvPos = event->pos();
        cursor.setShape(Qt::ClosedHandCursor);
        this->setCursor(cursor);
    }
    return QWidget::mousePressEvent(event);
}

void MicroToDo::mouseMoveEvent(QMouseEvent *event){
    if(moving) move(event->globalPos() - reltvPos);
    return QWidget::mouseMoveEvent(event);
}

void MicroToDo::mouseReleaseEvent(QMouseEvent *event){
    moving = false;
    cursor.setShape(Qt::ArrowCursor);
    this->setCursor(cursor);
    return QWidget::mouseReleaseEvent(event);
}

void MicroToDo::showSettings(){
    Settings* set = new Settings(this);
    set->show();
}

void MicroToDo::closeEvent(QCloseEvent *e){
    e->ignore();
}

void MicroToDo::resizeEvent(QResizeEvent *event){
    ui->ExitLabel->move(event->size().width() - ui->ExitLabel->size().width() - 35, 10);
}

void MicroToDo::getData(){
    static task t;
    query->exec("Select deltaLink from nextLink");
    query->next();
    selectUrl = query->value(0).toString();
    //qDebug() << selectUrl;
    req->setHeader("Authorization", token);
    //req->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    QString result = req->Get(selectUrl);
    if(result == "Host requires authentication")
        refreshToken();
    result = req->Get(selectUrl);
    QJsonDocument json = QJsonDocument::fromJson(result.toLocal8Bit().data());
    QJsonArray js_list = json.object().find("value")->toArray();
    QString nextlink = json.object().find("@odata.deltaLink")->toString();
    if(nextlink.isEmpty())
        nextlink = json.object().find("@odata.nextLink")->toString();
    for(auto js : js_list){
        QJsonDocument t_json = QJsonDocument::fromVariant(js.toVariant());
        t.importance = t_json.object().find("importance")->toString();
        t.isReminderOn = t_json.object().find("isReminderOn")->toBool();
        t.status = t_json.object().find("status")->toString();
        t.title = t_json.object().find("title")->toString();
        t.id = t_json.object().find("id")->toString();
        haveUpdate = true;

        changed.insert(t.id);

        updateTasks(t);
    }
    if(!nextlink.isEmpty()){
        if(haveUpdate){
            query->exec(QString("Update nextLink SET deltaLink='%1'").arg(nextlink));
            haveUpdate = false;
        }
    }
}

void MicroToDo::updateTasks(const task &todo){
    query->exec(QString("select title from tasks where id='%1' ORDER BY Query DESC").arg(todo.id));
    if(query->next()){
        query->exec(QString("Update tasks SET title='%1',importance='%2',isReminderOn=%3,status='%4',query=(select max(Query) from tasks)+1 where id='%5'").arg(todo.title).arg(todo.importance).arg((int)todo.isReminderOn).arg(todo.status).arg(todo.id));
        if(todo.status == "completed" && doingTask.contains(todo.id)){
            doingTask[todo.id]->hide();
            ui->flagLayout->removeWidget(doingTask[todo.id]);
            doingTask[todo.id]->deleteLater();
            doingTask.remove(todo.id);
        }else if(todo.status == "notStarted" && !doingTask.contains(todo.id)){
            doingTask[todo.id] = new ClickableLabel(QString(" ●\t") + todo.title, this);
            doingTask[todo.id]->id = todo.id;
            doingTask[todo.id]->setMinimumHeight(50);
            ui->flagLayout->insertWidget(0, doingTask[todo.id]);
            //doingTask[todo.id]->raise();
        }
        //qDebug() << "Update " << todo.title;
    }else{
        query->exec(QString("Insert into tasks(id,title,importance,isReminderOn,status) values('%1','%2','%3',%4,'%5')").arg(todo.id).arg(todo.title).arg(todo.importance).arg((int)todo.isReminderOn).arg(todo.status));
        if(todo.status == "notStarted" && !doingTask.contains(todo.id)){
            doingTask[todo.id] = new ClickableLabel(QString(" ●\t") + todo.title, this);
            doingTask[todo.id]->id = todo.id;
            doingTask[todo.id]->setMinimumHeight(50);
            ui->flagLayout->insertWidget(0, doingTask[todo.id]);
        }
        //qDebug() << "Insert " << todo.title;
    }
}

void MicroToDo::refreshData(){
    if(showDone){
        query->exec(QString("select id,title from tasks where status='%1' ORDER BY Query ASC").arg("completed"));
        while (query->next()) {
            QString id = query->value(0).toString();
            if(!compledTask.contains(id)){
                compledTask.insert(id, new ClickableLabel(this));
                compledTask[id]->id = id;
                compledTask[id]->completed = true;
                compledTask[id]->setMinimumHeight(50);
                compledTask[id]->setText(QString(" ●\t") + query->value(1).toString());
                ui->flagLayout->insertWidget(0, compledTask[id]);
            }
            compledTask[id]->setText(QString(" ●\t") + query->value(1).toString());
        }
    }else{
        query->exec(QString("select id,title from tasks where status='%1' ORDER BY Query ASC").arg("notStarted"));
        //qDebug() << "changed" << changed;
        while (query->next()) {
            QString id = query->value(0).toString();
            if(!doingTask.contains(id)){
                doingTask.insert(id, new ClickableLabel(this));
                doingTask[id]->id = id;
                doingTask[id]->setMinimumHeight(50);
                doingTask[id]->setText(QString(" ●\t") + query->value(1).toString());
                ui->flagLayout->insertWidget(0, doingTask[id]);
            }else
                doingTask[id]->show();
            doingTask[id]->setText(QString(" ●\t") + query->value(1).toString());
        }
    }
}

void MicroToDo::on_addLabel_clicked(){
    ui->addLine->clear();
    ui->stackedWidget->setCurrentIndex(1);
    ui->addLine->setFrame(false);
}


void MicroToDo::on_addLine_editingFinished(){
    if(!ui->addLine->hasFocus()){
        ui->addLine->setFrame(true);
        ui->stackedWidget->setCurrentIndex(0);
        QString title = ui->addLine->text();
        QString url = QString("https://graph.microsoft.com/v1.0/me/todo/lists/%1/tasks").arg(listId);
        QString jsStr = QString("{\"title\":\"%1\"}").arg(title);
        QJsonDocument jsd = QJsonDocument::fromJson(jsStr.toUtf8().data());
        qDebug() << jsStr;
        req->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        req->setHeader("Authorization", token);
        req->PostJson(url, jsd.object(), false);
    }
}

